#include "MediaListModel.h"
#include "qstringlist.h"
#include "modelViewRules.h"

#include "qfileinfo.h"
#include "MediaSourceFactory.h"
#include "testableAssert.h"


#define INVALID_FILE    -1

/************************************************/
/* CMediaListModel                              */
/*  constructor                                 */
/************************************************/
MediaListModel::MediaListModel(MediaSourceFactoryInterface *mediaFactory,
                               QObject *parent) :
   QAbstractListModel(parent),
   m_mediaFactory(mediaFactory)
{
   Q_ASSERT(mediaFactory != NULL);
}

MediaListModel::~MediaListModel()
{
   delete m_mediaFactory;
}

/************************************************/
/* rowCount                                     */
/*  return the number of elemets                */
/************************************************/
int MediaListModel::rowCount(const QModelIndex &/*parent*/) const
{
   return m_mediaList.count();
}


const AbstractMediaSource *MediaListModel::mediaData(const QModelIndex &index) const
{
   const AbstractMediaSource *data = NULL;

   // chek if params are valid
   T_ASSERT_REP( (index.isValid()) && (index.row() < m_mediaList.count()),
                 QString("MediaListModel::mediaData: invalid index %1").arg(index.row()));

   data = m_mediaList.at( index.row() );

   return data;
}


QVariant MediaListModel::data(const QModelIndex &index, int role) const
{
   QVariant data = QVariant();

   /* chek if params are valid */
   if ((index.isValid()) && (index.row() < m_mediaList.count()))
   {
      int row = index.row();
      QString fullPath = m_mediaList.at(row)->fileName();

      switch (role)
      {
      case Qt::DisplayRole:
      case modelViewRules::StringId:
      case modelViewRules::Binary:
         data = QVariant( m_mediaList.at(row)->stringId() );
         break;
      case Qt::ToolTipRole:
         /* show complete file path */
         data = QVariant( fullPath );
         break;

      case modelViewRules::Serialize:
         data = QVariant( fullPath );
         break;
      }
   }

   return data;
}

bool MediaListModel::setData(const QModelIndex &index, const AbstractMediaSource *value)
{
   if( index.row() < m_mediaList.count())
   {
      // store value in list
      m_mediaList[index.row()] = value;
   }

   // notify views
   emit dataChanged( index, index);

   return false;
}

/************************************************/
/* flags                                        */
/*  return settings for specific item           */
/************************************************/
Qt::ItemFlags MediaListModel::flags(const QModelIndex &index) const
{
   Qt::ItemFlags flags = 0;

   if (index.isValid())
   {
      flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
   }

   return  flags;
}

/**
 * normally a media file is identified by the base name of file (with
 * extention). In case of conflict, a tail with a sequence number is added.
 * Confilcts may be due to media in different folder with same name or to using
 * the same media more than once.
 * @param full path of media file
 * @return media unique identifier
 */
QString MediaListModel::createStringId(const QString &filename)
{
   QString baseName = QFileInfo(filename).fileName();
   int baseNameUsage = 0;

   QString stringId = baseName; /* default case */

   foreach (const AbstractMediaSource *source, m_mediaList )
   {
      if (source->stringId().startsWith(baseName))
      {
         baseNameUsage++;
      }
   }

   if (baseNameUsage > 0)
   {
      stringId.append( QString(" (%1)").arg( baseNameUsage + 1 ) );
   }

   return stringId;
}

void MediaListModel::addMediaFiles(const QStringList &fileNames)
{
   foreach( QString file, fileNames)
   {
      addMediaFile( file);
   }
}

void MediaListModel::addMediaFile(const QString &fileName)
{
   AbstractMediaSource *mediaSrc;
   mediaSrc = m_mediaFactory->buildMediaSource( fileName, createStringId(fileName));

   /* insert at the end of model */
   int position = rowCount();

   insertRow( position );
   setData( index( position, 0), mediaSrc );
}


QModelIndex MediaListModel::indexForData(const AbstractMediaSource *data)
{
   QModelIndex index;

   int position = m_mediaList.indexOf( data );

   if (position == -1)
   {
      // item not found
      index = QModelIndex();
   }
   else
   {
      index = this->index( position );
   }

   return index;
}


/************************************************/
/* insertRow                                    */
/*  insert a row in the model in the given      */
/*  position                                    */
/************************************************/
bool MediaListModel::insertRow(int row, const QModelIndex &/*parent*/)
{
   bool ret = false;

   if ((row >= 0) && (row <=  m_mediaList.count()))
   {
      // insert a dummy source
      m_mediaList.insert( row, NULL );
      emit layoutChanged();

      ret = true;
   }

   return ret;
}

/************************************************/
/* removeRows                                   */
/*  remove a given number of rows from model,   */
/*  starting from index 'row'                   */
/************************************************/
bool MediaListModel::removeRows(int row, int count,
                                const QModelIndex &/*parent*/ )
{
   Q_ASSERT( row >= 0);
   Q_ASSERT( (row + count) <= rowCount() );

   if (count > 0)
   {
      beginRemoveRows(QModelIndex(), row, row + count - 1);

      for (int i = 0; i < count; ++i)
      {
         /* each 'remove' operation shitfs next element */
         m_mediaList.removeAt( row );
      }

      endRemoveRows();
      emit layoutChanged();
   }

   return true;
}

/**
 * @brief base class reimplementation. Source and destination indexes are ignored.
 * @param sourceRow - first row to be moved
 * @param count - number of rows moved from \p sourceRow
 * @param destinationChild - final position of the first row moved
 *
 * @return true if succesfully moved; false otherwise
 */
bool MediaListModel::moveRows(const QModelIndex &, int sourceRow, int count,
                              const QModelIndex &, int destinationChild)
{
   bool moveDone = false;

   if ((sourceRow >= 0) && (sourceRow < rowCount()) &&
       (destinationChild >= 0) && (destinationChild < rowCount()) &&
       (count > 0) && (count <= rowCount())
      )
   {
      moveRows( sourceRow, count, destinationChild);
      moveDone = true;
   }

   return moveDone;
}

QModelIndex MediaListModel::searchById(const QString &stringId)
{
   QModelIndex target = QModelIndex();

   for (int i=0; (i < m_mediaList.length()) && (target == QModelIndex()); i++)
   {
      if (m_mediaList.at(i)->stringId() == stringId)
      {
         target = index(i);
      }
   }

   return target;
}

/**
 * @brief overload function that assumes that parameters are consistent
 */
void MediaListModel::moveRows(int sourceRow, int count, int destinationRow)
{
   for (int i=0; i < count; i++)
   {
      m_mediaList.move( sourceRow + i, destinationRow + i );
   }

   /* this avoids need for BeginMoveRow and EndMoveRow */
   layoutChanged();
}
